﻿using System;
using System.Collections.Generic;
using System.Dynamic;

namespace Jil.DeserializeDynamic
{
	sealed partial class JsonObject : IDynamicMetaObjectProvider
	{
		private enum JsonObjectType : byte
		{
			Array = 0,
			Object = 1,
			ObjectMember = 2,
			True = 3,
			False = 4,
			String = 5,
			Number = 6,
			FastNumber = 7
		}

		internal static readonly JsonObject True = new JsonObject { Type = JsonObjectType.True };
		internal static readonly JsonObject False = new JsonObject { Type = JsonObjectType.False };

		private JsonObjectType Type;
		private string StringValue;
		private double NumberValue;
		private JsonObject Parent;
		private List<JsonObject> ArrayValue;
		private Dictionary<string, JsonObject> ObjectMembers;

		private bool FastNumberNegative;
		private ulong FastNumberPart1;
		private uint FastNumberPart2;
		private byte FastNumberPart2Length;
		private long FastNumberPart3;

		private JsonObject MemberPart1;
		private JsonObject MemberPart2;

		private Options Options;

		internal static JsonObject ForString(string str, Options options)
		{
			return new JsonObject { Type = JsonObjectType.String, StringValue = str, Options = options };
		}

		internal static JsonObject ForNumber(double num, Options options)
		{
			return new JsonObject { Type = JsonObjectType.Number, NumberValue = num, Options = options };
		}

		internal static JsonObject ForFastNumber(bool neg, ulong a, uint b, byte bLen, long c, Options options)
		{
			return new JsonObject { Type = JsonObjectType.FastNumber, FastNumberNegative = neg, FastNumberPart1 = a, FastNumberPart2 = b, FastNumberPart2Length = bLen, FastNumberPart3 = c, Options = options };
		}

		internal static JsonObject NewArray(JsonObject parent)
		{
			return new JsonObject { Type = JsonObjectType.Array, Parent = parent, ArrayValue = new List<JsonObject>() };
		}

		internal static JsonObject NewObject(JsonObject parent)
		{
			return new JsonObject { Type = JsonObjectType.Object, Parent = parent, ObjectMembers = new Dictionary<string, JsonObject>() };
		}

		internal static JsonObject NewObjectMember(JsonObject parent)
		{
			return new JsonObject { Type = JsonObjectType.ObjectMember, Parent = parent };
		}

		internal JsonObject Pop()
		{
			if (Parent == null) return this;

			return Parent;
		}

		internal void Put(JsonObject other)
		{
			switch (Type)
			{
				case JsonObjectType.Array: ArrayValue.Add(other); return;
				case JsonObjectType.ObjectMember:
					if (MemberPart1 == null)
					{
						MemberPart1 = other;
					}
					else
					{
						if (MemberPart2 == null)
						{
							MemberPart2 = other;
							this.Parent.ObjectMembers.Add(MemberPart1.StringValue, MemberPart2);
						}
						else
						{
							throw new InvalidOperationException();
						}
					}
					return;
			}

			throw new InvalidOperationException();
		}

		public override string ToString()
		{
			return JSON.SerializeDynamic(this, Options.ISO8601PrettyPrint);
		}
	}
}